package pathfinding

import (
	"bytes"
	"fmt"
	"math/rand"
	"sync"

	"gddgame.cc/galaxy/utils"
	"gddgame.cc/galaxy/utils/serialize"
)

type PathNode interface {
	GetItem() []byte
	GetIdentity() int
	GetType() byte
	Exist() bool
}

type PathSeatNode interface {
	PathNode
	GetSeat() *Seat
	GetFixed() *Fixed
}

type PathMask interface {
	CanPath(point *Vector2, node PathSeatNode) bool
	GetIdentity() int
	Release()
	NeedChangeTarget() bool
	IsTargetPositions(point *Vector2) bool
}

type PathPreSeat interface {
	Init(grid *Grid, start *Vector2, instance RuleInstance)
	GetItem() []byte
	GetMask() PathMask
	ProcessNode(start *Vector2, timestamp int64, node PathSeatNode, step int) (serialize.AutoIStruct, *BaseSeat)
	Release()
}

type GridMap interface {
	GetDirection(result *Vector2, x int16, y int16, dir byte)
	Direction(start *Vector2, end *Vector2) byte
	Distance(current *Vector2, goal *Vector2) float64
	DirectionCount() byte
}

type Grid struct {
	BoundsMaxX int16
	BoundsMinX int16
	BoundsMaxY int16
	BoundsMinY int16

	navigable [][]*DefineNodeContent
	// 用来确保移动数据成员逻辑
	members map[string]*SeatMember

	start *Vector2
	Lock  sync.RWMutex

	CostType []int
}

func NewGrid(start Vector2, n [][]*DefineNodeContent, m map[string]*SeatMember) *Grid {
	grid := &Grid{
		BoundsMaxX: int16(len(n) - 1),
		BoundsMinX: 0,
		BoundsMaxY: int16(len(n[0]) - 1),
		BoundsMinY: 0,
		navigable:  n,
		members:    m,
		start:      &start,
		Lock:       sync.RWMutex{},
		CostType:   make([]int, 16),
	}
	return grid
}

func (grid *Grid) NodeCost(point *Vector2) int {
	node := grid.navigable[point.X][point.Y]
	if node == nil {
		return 0
	}
	return grid.CostType[node.Type]
}

func (grid *Grid) UpdateNode(point *Vector2, identity int, t byte) {
	grid.Lock.Lock()
	node := grid.navigable[point.X][point.Y]
	if node == nil {
		node = &DefineNodeContent{
			Type:     0,
			Identity: 0,
		}
		node.InitMutex()
		grid.navigable[point.X][point.Y] = node
	}
	grid.Lock.Unlock()
	node.mutex.Lock()
	node.Identity = identity
	node.Type = t
	node.mutex.Unlock()
}

func (grid *Grid) UpdateFixed(point *Vector2, identity int, t byte, view byte, fixItem []byte, timestamp int64) {
	grid.Lock.Lock()
	node := grid.navigable[point.X][point.Y]
	if node == nil {
		node = &DefineNodeContent{
			Type:     0,
			Identity: 0,
		}
		node.InitMutex()
		grid.navigable[point.X][point.Y] = node
	}
	grid.Lock.Unlock()
	node.mutex.Lock()
	if fixItem != nil {
		node.Fixed = &Fixed{
			Item:     fixItem,
			Identity: identity,
			Type:     t,
			View:     view,
		}
		if view > 0 {
			node.HasView = true
		}
	} else {
		node.Fixed = nil
		if node.HasView {
			if node.Seat == nil || (node.Seat.EndTime != 0 && node.Seat.EndTime < timestamp) || node.Seat.View == 0 {
				node.HasView = false
			}
		}
	}
	node.mutex.Unlock()
}

func (grid *Grid) UpdateSeat(item []byte, from *Vector2, to *Vector2, startTime int64, identity int, view byte, extend interface{}) bool {
	//if from != nil && to != nil {
	//fmt.Println("update seat", item, from, to, startTime, identity, extend)
	//}
	// 只有form就是结束占位
	// 只有to就是初次占位
	var member *SeatMember
	itemString := utils.ToString(item)
	grid.Lock.Lock()
	member, ok := grid.members[itemString]

	if !ok && from != nil && to != nil {
		// 移动，但不存在
		grid.Lock.Unlock()
		fmt.Println("move later", item, from, to)
		return false
	} else if !ok {
		// 进入，但不存在
		member = &SeatMember{}
		grid.members[itemString] = member
	} else if ok && to == nil {
		// 离开，但是存在
		delete(grid.members, itemString)
	}
	grid.Lock.Unlock()
	//if member.Next != nil && from != nil {
	//	if !member.Next.Equals(from) && !member.Last.Equals(from) {
	//		a := &Vector2{}
	//		a.Copy(from)
	//		grid.start.Restore(a)
	//		b := &Vector2{}
	//		b.Copy(member.Next)
	//		grid.start.Restore(b)
	//		fmt.Println("move error", item, member.Last, member.Next, member.Time, b.Encode(), from, to, startTime, a.Encode())
	//	}
	//}
	if to != nil {
		// 更新
		member.Last = from
		member.Next = to
		member.Time = startTime
	} else if from != nil && member.Last != nil && !member.Last.Equals(from) {
		from = member.Last
	}
	//fmt.Println("member", member)
From:
	// Lock对应auto-findEmpty实现坐标锁定，不允许其他人占用，但可以通过
	// Pre对应正常move，对于即将进入的节点原子性操作，立马会调用UpdateSeat更新掉
	if from != nil {
		grid.Lock.RLock()
		node := grid.navigable[from.X][from.Y]
		grid.Lock.RUnlock()
		if node != nil {
			node.mutex.Lock()
			if node.Lock != nil && bytes.Equal(node.Lock.Item, item) {
				// 如果lock没到就离开，取消lock
				node.Lock = nil
			} else if node.Seat != nil {
				//if from != nil && to != nil {
				//fmt.Println("from", node.Seat.Item, item, from)
				//}
				// 无论startTime多少：有没有进入格子都可以设定endTime
				if node.Seat.Other != nil && bytes.Equal(node.Seat.Other.Item, item) {
					//fmt.Println("from seat other", startTime)
					node.Seat.Other.EndTime = startTime
				} else if bytes.Equal(node.Seat.Item, item) {
					//fmt.Println("from seat update", startTime)
					node.Seat.EndTime = startTime
					node.Lock = nil
				}
			}
			if node.HasView {
				if node.Fixed == nil || node.Fixed.View == 0 {
					node.HasView = false
				}
			}
			node.mutex.Unlock()
		}
		if to == nil && member.Next != nil {
			// 对于移动到一半解除占位，同时需要去移除移动的下一点
			from = member.Next
			member.Next = nil
			goto From
		}
	}

	if to != nil {
		grid.Lock.Lock()
		node := grid.navigable[to.X][to.Y]
		if node == nil {
			node = &DefineNodeContent{
				Type:     0,
				Identity: 0,
			}
			node.InitMutex()
			grid.navigable[to.X][to.Y] = node
		}
		grid.Lock.Unlock()
		node.mutex.Lock()
		seat := &Seat{
			Item:         item,
			StartTime:    startTime,
			EndTime:      0,
			Identity:     identity,
			View:         view,
			InlineExtend: extend,
		}
		// 任何情况移动上来，pre都取消
		//if node.Pre == nil && node.Lock == nil {
		//	fmt.Println("move pre null", item, to)
		//} else if !bytes.Equal(node.Pre.Item, seat.Item) {
		//	fmt.Println("move pre err", item, to)
		//}
		node.Pre = nil

		if node.Lock != nil {
			if bytes.Equal(node.Lock.Item, seat.Item) {
				// 不考虑other的情况，如果item相同通过的部队应该已经离开
				node.Lock = nil
				node.Seat = seat
			} else {
				if node.Seat == nil || (node.Seat.EndTime > 0 && node.Seat.EndTime < startTime) {
					node.Seat = seat
				} else {
					node.Seat.Other = seat
				}
				// 根据最低限度放弃锁定，如果要移动上，则按正常移动判断
				if node.Lock.StartTime < startTime {
					node.Lock = nil
					fmt.Println("lock timeout")
				}
			}
		} else if node.Seat != nil {
			if !bytes.Equal(node.Seat.Item, seat.Item) {
				if node.Seat.EndTime == 0 || node.Seat.EndTime > startTime {
					//fmt.Println("to seat other", startTime)
					node.Seat.Other = seat
				} else {
					//fmt.Println("to seat update", startTime)
					node.Seat = seat
				}
			} else {
				// 重新更新时间
				//fmt.Println("to seat update 1")
				if node.Seat.EndTime > 0 {
					node.Seat.StartTime = startTime
					node.Seat.EndTime = 0
				}
				if extend != nil {
					node.Seat.InlineExtend = extend
				}
				node.Seat.View = view
			}
		} else {
			//fmt.Println("to seat base")
			node.Seat = seat
		}
		if !node.HasView {
			if node.Seat.View > 0 {
				node.HasView = true
			}
		}
		node.mutex.Unlock()
	}
	return true
}
func (grid *Grid) GetLinksBy(relation byte, position Vector2) [][]byte {
	if !grid.InBounds(position.X, position.Y) {
		return nil
	}
	grid.Lock.RLock()
	node := grid.navigable[position.X][position.Y]
	if node == nil {
		grid.Lock.RUnlock()
		return nil
	}
	grid.Lock.RUnlock()
	//node.mutex.RLock()
	if node.LinkMap == nil {
		//node.mutex.RUnlock()
		return nil
	}
	var manager *RelationLinkManager
	if len(node.LinkMap) <= int(relation) {
		manager = &RelationLinkManager{
			Relation: relation,
			Items:    [][]byte{},
		}
		manager.InitMutex()
		node.mutex.Lock()
		node.LinkMap = append(node.LinkMap, make([]*RelationLinkManager, int(relation)-len(node.LinkMap)+1)...)
		node.LinkMap[relation] = manager
		node.mutex.Unlock()
	} else {
		manager = node.LinkMap[relation]
	}
	return manager.Items
}
func (grid *Grid) GetLinks(position Vector2) RelationLinkManagerSlice {
	if !grid.InBounds(position.X, position.Y) {
		return nil
	}
	grid.Lock.RLock()
	node := grid.navigable[position.X][position.Y]
	if node == nil {
		grid.Lock.RUnlock()
		return nil
	}
	grid.Lock.RUnlock()
	node.mutex.RLock()
	if node.LinkMap == nil {
		node.mutex.RUnlock()
		return RelationLinkManagerSlice{}
	}
	s := RelationLinkManagerSlice{}
	for _, r := range node.LinkMap {
		s = append(s, *r)
	}
	node.mutex.RUnlock()
	return s
}
func (grid *Grid) AddLinks(target []byte, relation byte, positions Vector2Slice) {
	var manager *RelationLinkManager
	for _, position := range positions {
		if !grid.InBounds(position.X, position.Y) {
			continue
		}
		grid.Lock.Lock()
		node := grid.navigable[position.X][position.Y]
		if node == nil {
			node = &DefineNodeContent{
				Type:     0,
				Identity: 0,
			}
			node.InitMutex()
			grid.navigable[position.X][position.Y] = node
		}
		grid.Lock.Unlock()
		if node.LinkMap == nil {
			node.mutex.Lock()
			node.LinkMap = []*RelationLinkManager{}
			manager = &RelationLinkManager{
				Relation: relation,
				Items:    [][]byte{},
			}
			manager.InitMutex()

			node.LinkMap = append(node.LinkMap, make([]*RelationLinkManager, int(relation)-len(node.LinkMap)+1)...)
			node.LinkMap[relation] = manager

			node.mutex.Unlock()
		} else {
			if len(node.LinkMap) <= int(relation) {
				manager = &RelationLinkManager{
					Relation: relation,
					Items:    [][]byte{},
				}
				manager.InitMutex()
				node.mutex.Lock()
				node.LinkMap = append(node.LinkMap, make([]*RelationLinkManager, int(relation)-len(node.LinkMap)+1)...)
				node.LinkMap[relation] = manager
				node.mutex.Unlock()
			} else {
				manager = node.LinkMap[relation]
			}
		}
		if manager == nil {
			continue
		}
		manager.mutex.Lock()
		l := len(manager.Items)
		if l == 0 {
			manager.Items = append(manager.Items, target)
		} else {
			i := -1
			for index, item := range manager.Items {
				if !bytes.Equal(target, item) {
					continue
				}
				i = index
			}
			if i == -1 {
				manager.Items = append(manager.Items, target)
			}
		}

		manager.mutex.Unlock()
	}
}
func (grid *Grid) RemoveLinks(target []byte, relation byte, positions Vector2Slice) {
	for _, position := range positions {
		if !grid.InBounds(position.X, position.Y) {
			continue
		}
		grid.Lock.RLock()
		node := grid.navigable[position.X][position.Y]
		if node == nil {
			grid.Lock.RUnlock()
			continue
		}
		grid.Lock.RUnlock()
		if node.LinkMap == nil {
			continue
		}

		var manager *RelationLinkManager
		if len(node.LinkMap) <= int(relation) {
			continue
		} else {
			manager = node.LinkMap[relation]
		}
		l := len(manager.Items)
		if l == 0 {
			continue
		}
		i := -1
		manager.mutex.Lock()
		for index, item := range manager.Items {
			if !bytes.Equal(target, item) {
				continue
			}
			i = index
		}
		if i >= 0 {
			if i == 0 {
				manager.Items = manager.Items[1:]
			} else if i == l-1 {
				manager.Items = manager.Items[0:i]
			} else {
				manager.Items = append(manager.Items[:i], manager.Items[i+1:]...)
			}
		}

		manager.mutex.Unlock()
	}
}

func (grid *Grid) HasView(start *Vector2, target *Vector2, identity int, distance int, self bool, timestamp int64) bool {
	center := start.EvenQToCube()
	targetCube := target.EvenQToCube()
	for i := 0; i <= distance; i++ {
		l := HexRingDistance(&center, i, 4)
		for _, k := range l {
			point := k.ToEvenQ()
			if !grid.InBounds(point.X, point.Y) {
				continue
			}
			node := grid.navigable[point.X][point.Y]
			if node == nil {
				continue
			}
			if !node.HasView {
				continue
			}
			d := 0
			node.mutex.RLock()
			if node.Fixed != nil {
				d = DistanceWithCube(&targetCube, &k)
				if int(node.Fixed.View) >= d {
					isPartner := node.Fixed.Identity&identity > 0
					if self && isPartner {
						node.mutex.RUnlock()
						return true
					} else if !self && !isPartner {
						node.mutex.RUnlock()
						return true
					}
				}
			}
			if node.Seat != nil && (node.Seat.EndTime == 0 || node.Seat.EndTime > timestamp) {
				if d == 0 {
					d = DistanceWithCube(&targetCube, &k)
				}
				if int(node.Seat.View) >= d {
					isPartner := node.Seat.Identity&identity > 0
					if self && isPartner {
						node.mutex.RUnlock()
						return true
					} else if !self && !isPartner {
						node.mutex.RUnlock()
						return true
					}
				}
			}
			node.mutex.RUnlock()
		}
	}
	return false
}

func (grid *Grid) GetItem(position Vector2, timestamp int64) (byte, []byte, int) {
	seat, node := grid.GetSeatWithPosition(&position, timestamp)
	if seat != nil {
		if node != nil {
			return node.Type, seat.Item, seat.Identity
		}
		return 0, seat.Item, seat.Identity
	}
	if node != nil {
		if node.Fixed != nil {
			return node.Type, node.Fixed.Item, node.Fixed.Identity
		}
		return node.Type, nil, node.Identity
	}
	return 0, nil, 0
}
func (grid *Grid) GetItemList(positions []Vector2, timestamp int64) ([]byte, [][]byte, []int) {
	t := make([]byte, len(positions))
	s := make([][]byte, len(positions))
	i := make([]int, len(positions))
	for index, p := range positions {
		t[index], s[index], i[index] = grid.GetItem(p, timestamp)
	}
	return t, s, i
}
func (grid *Grid) GetFixed(position Vector2) ([]byte, int) {
	ok, node := grid.Node(&position)
	if !ok {
		return nil, 0
	}
	if node != nil && node.Exist() {
		fixed := node.GetFixed()
		if fixed != nil {
			return fixed.Item, fixed.Identity
		}
		return nil, 0
	}
	return nil, 0
}
func (grid *Grid) GetFixedList(positions []Vector2) ([][]byte, []int) {
	s := make([][]byte, len(positions))
	i := make([]int, len(positions))
	for index, p := range positions {
		s[index], i[index] = grid.GetFixed(p)
	}
	return s, i
}
func (grid *Grid) GetSeatWithPosition(position *Vector2, timestamp int64) (*Seat, *DefineNodeContent) {
	if !grid.InBounds(position.X, position.Y) {
		return nil, nil
	}
	grid.Lock.RLock()
	node := grid.navigable[position.X][position.Y]
	if node == nil {
		grid.Lock.RUnlock()
		return nil, nil
	}
	seat := grid.GetSeatWithNode(node, timestamp)
	grid.Lock.RUnlock()
	return seat, node
}

func (grid *Grid) GetSeatWithNode(node *DefineNodeContent, timestamp int64) *Seat {
	if node.Seat != nil {
		if node.Seat.EndTime == 0 || node.Seat.EndTime > timestamp {
			return node.Seat
		}
	}
	return nil
}

func (grid *Grid) CanAndLock(position *Vector2, timestamp int64, request PathPreSeat, step int) (bool, bool, serialize.IStruct) {
	var seat *Seat
	grid.Lock.Lock()
	node := grid.navigable[position.X][position.Y]
	if node == nil {
		node = &DefineNodeContent{
			Type:     0,
			Identity: 0,
		}
		node.InitMutex()
		grid.navigable[position.X][position.Y] = node
	}
	grid.Lock.Unlock()
	node.mutex.RLock()
	if !request.GetMask().CanPath(position, node) {
		node.mutex.RUnlock()
		return false, false, nil
	}
	if node.Lock != nil || node.Pre != nil {
		//fmt.Println("seat", node.Lock, node.Pre)
		node.mutex.RUnlock()
		return false, false, nil
	}
	node.mutex.RUnlock()
	node.mutex.Lock()
	seat = grid.GetSeatWithNode(node, timestamp)
	if seat != nil {
		//fmt.Println("seat", timestamp, seat)
		node.mutex.Unlock()
		return true, false, nil
	}
	response, nn := request.ProcessNode(position, timestamp, node, step)
	if nn != nil {
		node.Lock = nn
	}
	node.mutex.Unlock()
	return true, true, response
}

func (grid *Grid) CanAndPreMove(current *Vector2, target *Vector2, timestamp int64, request PathPreSeat) serialize.AutoIStruct {
	grid.Lock.Lock()
	node := grid.navigable[target.X][target.Y]
	if grid.navigable[target.X][target.Y] == nil {
		node = &DefineNodeContent{
			Type:     0,
			Identity: 0,
		}
		node.InitMutex()
		grid.navigable[target.X][target.Y] = node
	}
	grid.Lock.Unlock()
	node.mutex.RLock()

	if !request.GetMask().CanPath(target, node) {
		//fmt.Println("can path", false, target, node)
		node.mutex.RUnlock()
		return nil
	}
	if node.Lock != nil && !bytes.Equal(node.Lock.Item, request.GetItem()) {
		node.mutex.RUnlock()
		fmt.Println("has lock", false, target, node)
		return nil
	}
	//if node.Pre != nil && !bytes.Equal(node.Pre.Item, request.GetItem()) {
	//	node.mutex.RUnlock()
	//	//fmt.Println("has pre", false, target, node)
	//	return false, nil
	//}
	node.mutex.RUnlock()
	node.mutex.Lock()
	response, nn := request.ProcessNode(target, timestamp, node, 1)
	//fmt.Println("can move", request.GetItem(), position, seat, nn)
	if nn != nil {
		node.Pre = nn
	}
	node.mutex.Unlock()
	return response
}

func (grid *Grid) CanAndPreRange(current *Vector2, timestamp int64, request PathPreSeat) bool {
	if !grid.InBounds(current.X, current.Y) {
		return false
	}
	grid.Lock.Lock()
	node := grid.navigable[current.X][current.Y]
	if grid.navigable[current.X][current.Y] == nil {
		node = &DefineNodeContent{
			Type:     0,
			Identity: 0,
		}
		node.InitMutex()
		grid.navigable[current.X][current.Y] = node
	}
	grid.Lock.Unlock()
	node.mutex.RLock()

	if !request.GetMask().CanPath(current, node) {
		//fmt.Println("can path", false, current, node)
		node.mutex.RUnlock()
		return false
	}
	if node.Lock != nil && !bytes.Equal(node.Lock.Item, request.GetItem()) {
		node.mutex.RUnlock()
		fmt.Println("has lock", false, current, node)
		return false
	}
	node.mutex.RUnlock()
	node.mutex.Lock()
	seat := grid.GetSeatWithNode(node, timestamp)
	if seat != nil {
		node.mutex.Unlock()
		return false
	}
	_, nn := request.ProcessNode(current, timestamp, node, 0)
	if nn != nil {
		node.Lock = nn
	}
	node.mutex.Unlock()
	return node.Lock != nil
}

func (grid *Grid) RandomPosition(canType int, canFix bool, canSeat bool, timestamp int64, result *Vector2) bool {
	x := rand.Int31n(int32(grid.BoundsMaxX))
	y := rand.Int31n(int32(grid.BoundsMaxY))
	for i := 0; i < 100; i++ {
		c := rand.Int31n(2)
		if c == 0 {
			x += 1
			y += 1
		} else {
			x -= 1
			y -= 1
		}
		if x < 0 || x >= int32(grid.BoundsMaxX) {
			x = rand.Int31n(int32(grid.BoundsMaxX))
			continue
		}
		if y < 0 || y >= int32(grid.BoundsMaxY) {
			y = rand.Int31n(int32(grid.BoundsMaxX))
			continue
		}
		node := grid.navigable[x][y]
		if node != nil {
			if canType > 0 && ((1<<(node.Type-1))&canType == 0) {
				continue
			} else if canType == 0 && node.Type > 0 {
				continue
			}
			if node.Fixed != nil && !canFix {
				continue
			}
			seat := grid.GetSeatWithNode(node, timestamp)
			if seat != nil && !canSeat {
				continue
			}
		}
		result.X = int16(x)
		result.Y = int16(y)
		return true
	}
	return false
}
func (grid *Grid) Node(point *Vector2) (bool, PathSeatNode) {
	if !grid.InBounds(point.X, point.Y) {
		return false, nil
	}
	node := grid.navigable[point.X][point.Y]
	return true, node
}

func (grid *Grid) n(point *Vector2, mask PathMask) (bool, *DefineNodeContent) {
	if !grid.InBounds(point.X, point.Y) {
		return false, nil
	}
	node := grid.navigable[point.X][point.Y]
	if node != nil && node.IsBoundary {
		return false, nil
	}
	return mask.CanPath(point, node), node
}

func (grid *Grid) N(point *Vector2, mask PathMask) bool {
	ok, node := grid.n(point, mask)
	if ok && node != nil {
		// 锁定后只能自己解锁
		if node.Lock != nil {
			return false
		}
	}
	return ok
}

func (grid *Grid) InBounds(x int16, y int16) bool {
	return x >= grid.BoundsMinX && x < grid.BoundsMaxX &&
		y >= grid.BoundsMinY && y < grid.BoundsMaxY
}

func TransformGrid(nav [][]byte) ([][]*DefineNodeContent, map[string]*SeatMember) {
	x := len(nav)
	y := len(nav[0])
	n := make([][]*DefineNodeContent, x)
	m := map[string]*SeatMember{}
	for i := 0; i < x; i++ {
		n[i] = make([]*DefineNodeContent, y)
	}
	for x, nodes := range nav {
		for y, node := range nodes {
			if node == 0 {
				node = 1
			} else {
				node = 0
			}
			n[x][y] = &DefineNodeContent{
				Type:     node,
				Identity: 0,
			}
			n[x][y].InitMutex()
		}
	}
	return n, m
}
